# -*- coding: utf-8 -*-
# ---
# jupyter:
#   jupytext:
#     formats: ipynb,py:light
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.4'
#       jupytext_version: 1.1.1
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---


# +
# 使用python的list进行循环遍历运算
def pySum():
    a = list(range(10000))
    b = list(range(10000))
    c = []
    for i in range(len(a)):
        c.append(a[i]**2 + b[i]**2)
        return c

# %timeit pySum()


# +
# 使用numpy进行向量化运算.
import numpy as np
def npSum():
    a = np.arange(10000)
    b = np.arange(10000)
    c = a**2 + b**2
    return c

# %timeit npSum()
'''
从运行结果可以看出: numpy的向量化运算的效率要远远高于python的循环列表计算.
1ms = 1000us.
'''


# +
# 1.2 创建ndarray数组
# 导入numpy库
import numpy as np
'''
创建ndarray数组的方式:
# method1: 基于list或tuple
'''
# 一维数组
# 基于list
arr1 = np.array([1,2,3,4])
print(arr1)

# 基于tuple
arr_tuple = np.array((1,2,3,4))
print(arr_tuple)

# 二维数组(2*3)
arr2 = np.array([[1,2,4],[3,4,5]])
print(arr2)
"""
请注意：
• 一维数组用 print 输出的时候为 [1 2 3 4]，跟 python 的列表是有些差异的，没有“,”
• 在创建二维数组时，在每个子 list 外面还有一个“[]”，形式为“[[list1], [list2]]”
"""
# -




# +

# Method 2: 基于np.arange
# 一维数组
arr1 = np.arange(5)
print(arr1)
print(type(arr1))
# 二维数组
arr2 = np.array([np.arange(3),np.arange(3)])
print(arr2)
print(type(arr2))
# -



# +
# Method 3: 基于arange以及reshape创建多维数组
# 创建三维数组
arr = np.arange(24).reshape(2,3,4)
arr

# 请注意：arange 的⻓度与 ndarray 的维度的乘积要相等，即 24 = 2X3X4
# 用 numpy.random 创建数组的方法
# -



# 1.3 Numpy的数值类型
np.int8(12.334)

np.float64(12)

np.float(True)

bool(1)

# 在创建ndarray数组时,可以指定数值类型
a = np.arange(5,dtype=float)
a

# 请注意: 复数不能转换成为整数类型或者浮点数.
# float(42 + 1j) #TypeError: can't convert complex to float





# 1.4 ndarray数组的属性
# dtype属性,ndarray数组的数据类型。
np.arange(4,dtype=float)

# 'D' 表示复数类型
np.arange(4,dtype='D')

np.array([1.22,3.45,6,779],dtype='int8')

# ndim属性,数组维度的数量
a = np.array([[1,2,3],[7,8,9]])
a.ndim

# shape属性,数组对象的尺度,对于矩阵,即 n 行 m 列,shape 是一个元组（tuple）
a.shape

# size属性用来保存元素的数量,相当于shape中nXm的值.
a.size

# itemsize 属性返回数组中各个元素所占用的字节数大小。
a.itemsize

# nbytes 属性，如果想知道整个数组所需的字节数量，可以使用 nbytes 属性。其值等于数组的 size 属性值乘以 itemsize 属性值。
a.nbytes

b = np.arange(24).reshape(4,6)
print(b)
print(b.T)

# 复数的实部和虚部属性，real 和 imag 属性
d = np.array([1.2+2j,2+3j])
d 

# real属性返回数组的实部
d.real

# imag 属性返回数组的虚部
d.imag

# flat 属性，返回一个 numpy.flatiter 对象，即可迭代的对象。
e = np.arange(6).reshape(2,3)
e

f = e.flat
f

for item in f:
    print(item)

# 可通过位置进行索引
f[2]


f[[1,4]]

# 进行赋值
e.flat=7
e

 


# 1.5 ndarray 数组的切片和索引
# 一维数组: 一维数组的切片和索引与python的list索引类似.
import numpy as np 
a = np.arange(7)
# a 
a[1:4]

# 每间隔2个取一个数
a[:6:2]

# 二维数组的切片和索引
b = np.arange(12).reshape(3,4)
b

b[0:3,0:2]

# 1.6 处理数组形状
## 形状转换
### reshape()和resize()
b.reshape(4,3)

b

b.resize(4,3)
b

# 函数resize()的作用跟reshape()类似,但是会改变所作用的数组.
# ravel()和flatten(),将多维数组转换成一维数组.如下:
b.ravel()

b.flatten()

b
'''
两者的区别在于返回拷⻉（**copy**）还是返回视图（**view**）**，flatten() 返回一份拷⻉，需要分配新的内存空间， 
对拷⻉所做的修改不会影响原始矩阵，而 ravel() 返回的是视图（view），会影响原始矩阵。 
'''

# flatten()返回的是拷贝,不影响原始数组
# 即数组"b" 没有发生变化
b.flatten()[2] = 20
b

# ravel()返回的是视图,会影响原始数组
# 即数组"b" 会发生变化
b.ravel()[2] = 20
b 

# 用tuple指定数组的形状
b.shape=(2,6)
b

# 转置
# 前面描述了数组转置的属性(T),也可以通过transpose()函数来实现.
b.transpose()

# 1.6.2 堆叠数组
b

c = b*2 
c 

# 水平叠加hstack()
np.hstack((b,c))


# column_stack()函数以列方式对数组进行叠加,功能类似hstack()
np.column_stack((b,c))

# 垂直叠加vstack()
np.vstack((b,c))


# row_stack()函数以行方式对数组进行叠加,功能类似vstack()
np.row_stack((b,c))

# concatenate()方法,通过设置axis的值来设置叠加方向
# axis=1时,沿水平方向叠加.
# axis=0时,沿垂直方向叠加.
np.concatenate((b,c),axis=1)

np.concatenate((b,c),axis=0)

# 深度叠加
arr_dstack = np.dstack((b,c))
print(arr_dstack.shape)
print('++'*20)
print(arr_dstack)
"""
叠加前,b和c均是shape为(2,6)的二维数组,叠加后,arr_dstack是shape为(2,6,2)的三维数组.
"""

# 1.6.3 数组的拆分
# 数组的拆分可以分为横向拆分,纵向拆分, 深度拆分.
# 涉及的函数为hsplit(),vsplit(),dsplit()以及split()
# print(b)
# print('='*30)
# 沿横向轴拆分(axis=1)
# print(np.hsplit(b,2))
# print('='*10)
# print(np.split(b,2,axis=1))

# 沿纵向轴拆分(axis=0)
print('...'*20)
print(np.vsplit(b, 2))
print('=='*20)
print(np.split(b,2,axis=0))


print(':'*20)
# 深度拆分
print(arr_dstack)
print('%'*20)
print(np.dsplit(arr_dstack,2))
''' 拆分的结果是原来的三维数组拆分成为两个二维数组。'''

print('++'*50)

# 1.7 数组的类型转换
# 数组转换成list,使用tolist()
print(b)
print('-'*20)
print(b.tolist())
# 转换成指定类型,astype()函数
print('--'*30)
print(b.astype(float))


# 1.8 numpy常用统计函数
'''
常用的函数如下, 请注意函数在使用时需要指定 axis 轴的方向，若不指定，默认统计整个数组。
- np.sum()，返回求和 
- np.mean()，返回均值 
- np.max()，返回最大值 
- np.min()，返回最小值 
-  np.ptp()，数组沿指定轴返回最大值减去最小值，即（max-min） 
-  np.std()，返回标准偏差（standard deviation） 
-  np.var()，返回方差（variance） 
-  np.cumsum()，返回累加值 
-  np.cumprod()，返回累乘积值 
'''
"""
print(';'*20)
print(b)
print(np.max(b))
# 沿axis=1轴方向统计
print('+'*20)
print(np.max(b,axis=1))
# 沿axis=0轴方向统计
print(np.max(b,axis=0))
print(np.min(b))
"""

# np.ptp() 返回整个数组的最大值减去最小值
"""
print('('*20)
print(np.ptp(b))
# 沿axis=0轴方向
print(np.ptp(b,axis=0))
# 沿axis=1轴方向
print(np.ptp(b,axis=1))
"""


# np.cumsum(),沿指定轴方向进行累加
"""
print('...'*20)
print(b.resize(4,3))
print(b)
print(')'*20)
print(np.cumsum(b,axis=1))
print(np.cumsum(b,axis=0))
"""



# np.cumprod(),沿指定轴方向进行累乘积
"""
print('...'*20)
print(np.cumprod(b,axis=1))
print(np.cumprod(b,axis=0))
"""


# 1.9 数组的广播
'''
当数组跟一个标量进行数学运算时，标量需要根据数组的形状进行扩展，然后执行运算。这个扩展的过程称
为"广播(broadcasting)"
'''
print(','*20)
print(b)
print('=='*20)
d = b + 2
print(d)
